import React from 'react';
import {Button, Col, Row} from 'antd';
import BaseComp from "../Base/BaseComp";
import {pick, find, isFunction, keys, map, isEmpty, forEach, forIn, findIndex, isUndefined} from "lodash-es";
import {EditableTree} from "../TreeComp";
import DetailTable from "../DetailTable";
import compConfig from './config'
import omit from "lodash-es/omit";
import {isNotEmpty, recursiveMap} from 'jh-utils'
import AbstractEditDataGrid from "../Base/AbstractEditDataGrid";
import PropTypes from "prop-types";

export default class EditableTreeTable extends AbstractEditDataGrid {

    static propTypes = {
        ...BaseComp.propTypes,
        tableProps: PropTypes.object
    };

    /**
     *
     * @type { EditableTree | null}
     */
    treeRef = null;
    /**
     *
     * @type { DetailTable | null}
     */
    tableRef = null;
    selectedNode = null;

    initState(props) {
        let superState = super.initState(props);
        return {
            ...superState,
        }
    }

    componentDidMount() {
        super.componentDidMount && super.componentDidMount();
        // 将本组件的实例存到 appins 的 dtIns 变量中，用于被 appins 在必要时调用
        const {appins, fieldName, customprops} = this.props;
        if (appins && isFunction(appins.setFieldInsByName) && !isEmpty(fieldName)) {
            appins.setFieldInsByName(fieldName, this, customprops);
        }
    }

    /**
     * 获取表格数据
     * @param onlyChange {boolean} 是否只获取改变了的数据。默认 true
     * @return {undefined|{comboVal, compVal}|[]}
     */
    getAjaxData(onlyChange = true) {

        if (this.treeRef && isFunction(this.treeRef.getAjaxData)) {
            return this.treeRef.getAjaxData(onlyChange);
        }
    }

    /**
     * 获取删除了的记录
     * @param fullRecord {boolean} 是否需要返回完整的记录。默认 false，只返回删除了的 id 数组
     * @return {[]}
     */
    getDeleted(fullRecord = false) {
        if (this.treeRef && isFunction(this.treeRef.getDeleted)) {
            return this.treeRef.getDeleted(fullRecord);
        }
    }

    /**
     * 更新某条记录的值。可指定更新哪些字段
     * @param record
     * @param updateFields {[string]} 需要更新的字段数组。如果为空，则更新 record 的所有字段
     * @param afterCb
     */
    updateRecordFields(record, updateFields = [], afterCb = null) {
        if (this.tableRef && isFunction(this.tableRef.updateRecordFields)) {
            this.tableRef.updateRecordFields(record, updateFields, afterCb);
        }
    }

    /**
     * 设置表格数据
     * @param data
     * @param afterCb
     * @param reMountTable {boolean} 是否需要重载 Table。默认 false
     * @param isInit {boolean} 是否初始化，如果是，则添加 defaultRowCnt 相关逻辑
     */
    setData(data, afterCb, reMountTable = false, isInit = false) {
        let newVal = recursiveMap(data, (child) => {
            if (undefined === child.key) {
                return {...child, key: child.isn}
            } else {
                return child;
            }
        });
        this.treeRef.setData(newVal, () => {
            this.sync2Table(this.treeRef.getAjaxData(false));
        });
    }

    /**
     * 设置表格的操作权限
     * @param viewConfig {{
     *     body,fields
     * }}
     * body 的结构：{
              "fieldName": "securityGroups",
              "addable": false, // 是否可添加
              "deletable": false, // 是否可删除
              "editable": false, // 是否可编辑
              "standard": false // 是否标准表体
            }
     * fields 的结构：[{
              "fieldName": "roles.corp", // 字段名
              "control": 2 // 对应 Config/FieldCtrl
            }]
     * @param afterCb
     */
    setViewConfig(viewConfig, afterCb) {
        if (this.tableRef && this.tableRef.setViewConfig) {
            this.tableRef.setViewConfig(viewConfig, afterCb)
        }
    }

    /**
     * 设置表格字段是否可用
     * @param fieldName {string}
     * @param enabled {boolean|function:boolean}
     * @param freshRender {boolean}
     * @param afterCb {function}
     */
    setFieldEnabled(fieldName, enabled, freshRender, afterCb) {

        if (this.tableRef && this.tableRef.setFieldEnabled) {
            this.tableRef.setFieldEnabled(fieldName, enabled, freshRender, afterCb);
        }
    }

    /**
     * 设置表体的操作权限
     * @param fields {[{
              "fieldName": string,
              "control": number
            }]}
     * @param freshRender {boolean} 是否需要刷新渲染组件。默认 false
     * @param afterCb
     */
    setFieldCtrlByFields(fields, freshRender = true, afterCb = null) {

        if (this.tableRef && this.tableRef.setFieldCtrlByFields) {
            this.tableRef.setFieldCtrlByFields(fields, freshRender, afterCb);
        }
    }

    /**
     * 设置【右侧表格】头部按钮是否可用
     * @param actions {string[]}
     * @param enabled {boolean}
     * @param afterCb
     */
    setActionEnabled(actions, enabled, afterCb) {
        if (this.tableRef && this.tableRef.setActionEnabled) {
            this.tableRef.setActionEnabled(actions, enabled, afterCb)
        }
    }

    /**
     * 设置“列渲染”
     * @param fieldName {string}
     * @param render {function(value: any, record:{}, rowIndex:number)}
     */
    setColRender(fieldName, render) {

        if (this.tableRef && isFunction(this.tableRef.setColRender)) {
            return this.tableRef.setColRender(fieldName, render);
        }
    }

    /**
     * 设置“字段单元格-编辑前”事件处理
     * @param fieldName {string}
     * @param func {function(compIns)} // compIns 是当前编辑组件的实例
     */
    addEditBeforeHandler(fieldName, func) {
        if (this.tableRef && isFunction(this.tableRef.addEditBeforeHandler)) {
            return this.tableRef.addEditBeforeHandler(fieldName, func);
        }
    }

    /**
     * 设置“字段单元格-编辑后”事件处理
     * @param fieldName {string}
     * @param func {function(value, record)}
     */
    addEditAfterHandler(fieldName, func) {
        if (this.tableRef && isFunction(this.tableRef.addEditAfterHandler)) {
            return this.tableRef.addEditAfterHandler(fieldName, func);
        }
    }

    /**
     * 设置“行操作”时的回调。例如：添加/删除/移动 行
     * @param func
     */
    setRowActionHandler(func) {
        if (this.tableRef && isFunction(this.tableRef.setRowActionHandler)) {
            return this.tableRef.setRowActionHandler(func);
        }
    }

    /**
     * 校验每行的必填字段
     * @param onlyChange {boolean} 是否只校验“有数据变化”的记录
     * @param moreProps {{prefixTxt: string, fieldTxtRender: (function(fieldName)), customRender: (function({fObj, record, line, fieldName}))}}
     * @param moreProps.customRender {function({fObj, record, line, fieldName})} 自定义错误信息的渲染方法
     * @param moreProps.data {[]} 如果传入这个参数，则以这个参数的值作为校验的数据源。否则默认取当前表格的数据进行校验
     * @return {{data: [], errors: []}}
     */
    verifyRequired(onlyChange = true, {customRender, data, local}) {
        let isByData = !isUndefined(data);
        const dataList = isByData ? data : this.getData(onlyChange);
        const errors = [];

        if (!isEmpty(dataList) && this.tableRef && this.tableRef.getRequiredFields) {
            // 表格的必填字段
            const requiredFields = this.tableRef.getRequiredFields();
            const colFields = this.tableRef.getColFields();

            if (!isEmpty(requiredFields) || !isEmpty(this.fieldVerifyCbObj)) {
                recursiveMap(dataList, (record, ind, parentObj, parentInd) => {

                    if (record) {
                        let line = ind + 1;
                        record.REMARK_KEY = `${!isEmpty(parentObj) ? parentObj.name + '->' : ''}${record.name}`;

                        forIn(requiredFields, (fObj, fieldName) => {
                            if (!isNotEmpty(record[fieldName])) {
                                // 't.verify.empty': '树节点【{node}】:【{fields}】不能为空',
                                let errTxt = this.myFormatMessage('t.verify.empty', {
                                    node: record.REMARK_KEY,
                                    fields: fObj[local] || fieldName
                                });

                                if (isFunction(customRender)) {
                                    errors.push(customRender({line, fieldName, desc: errTxt}));
                                } else {
                                    errors.push(errTxt);
                                }
                            }
                        });

                        forIn(this.fieldVerifyCbObj, (cbs, fieldName) => {
                            forEach(cbs, (cb) => {
                                if (isFunction(cb)) {
                                    let reObj = cb(record[fieldName], record);
                                    if (reObj) {
                                        let {error, isOk} = reObj;
                                        if (false === isOk) {
                                            if (isFunction(customRender)) {
                                                errors.push(customRender({
                                                    fObj: colFields[fieldName],
                                                    line,
                                                    fieldName,
                                                    desc: error
                                                }));
                                            } else {
                                                errors.push(error);
                                            }
                                        }
                                    }
                                }
                            })
                        })
                    }
                    return record;
                });
            }
        }

        return {data: dataList, errors};
    }

    /**
     * 选择节点之后的回调
     * @param keys {string[]} 选中的节点
     * @param event {{selected: boolean, selectedNodes, node, event}}
     */
    hdlSelectNode(keys, {node}) {
        // 如果点击的是同一个节点，则什么都不做
        if (this.selectedNode && this.selectedNode.key === node.key) return false;

        // 将左边数据 更新到右侧列表
        this.selectedNode = this.treeRef.getNodeByKey(node.key);
        // this.syncTree2Table(this.selectedNode);
        let tData = !isEmpty(node) ? node.children : [];

        // 更新右侧列表的数据
        const tableData = map(tData, (item) => {
            return omit(item, ['children', 'title']);
        });
        // console.log('hdlSelectNode tableData', tableData);
        this.tableRef.setData(tableData, null, true);
    }

    /**
     * 将 data 数据同步到右侧表格
     * @param data
     * @param afterCb
     */
    sync2Table(data, afterCb) {
        const tableData = map(data, (item) => {
            return omit(item, ['children']);
        });
        // console.log('hdlAddNode tableData', tableData);
        this.tableRef.setData(tableData, afterCb, true);
    }

    /**
     * 将node的子节点，更新到右侧表格
     */
    sync2TableByNode(node) {
        const tableData = map(node.children, (item) => {
            return omit(item, ['children']);
        });
        this.tableRef.setData(tableData, null, true);
    }

    getSelNode() {
        let selNode = !isEmpty(this.selectedNode) ? this.treeRef.getNodeByKey(this.selectedNode.key) : null;
        if (isEmpty(selNode)) {
            this.selectedNode = null;
        }
        return selNode;
    }

    hdlAddingNode(newNode, moreData) {
        const getDefaultRecord = this.getPropsByName('getDefaultRecord');
        if (isFunction(getDefaultRecord)) {
            // console.log('moreData', moreData)
            return getDefaultRecord(newNode, moreData, this);
        }
    }

    hdlAddNode(newNode, {treeData, parentNode}) {
        // 如果没有父节点，说明是添加第一级节点
        // 则将左侧第一级节点，更新到右侧
        if (isEmpty(parentNode)) {
            this.sync2Table(treeData);
        } else {
            // 有父节点

            // 如果有选中节点
            // 将当前选中节点的子节点，同步到右侧
            let selNode = this.getSelNode();
            if (!isEmpty(selNode)) {
                this.sync2TableByNode(selNode);
            } else {
                this.sync2Table(treeData);
            }
        }
    }

    hdlDelNode(delNode, event) {
        this.hdlAddNode(delNode, event);
    }

    hdlSaveNode(newNode, event) {
        let selNode = this.getSelNode();
        if (!isEmpty(selNode)) {
            this.sync2TableByNode(selNode);
        } else {
            this.hdlAddNode(newNode, event);
        }
    }

    /**
     * 编辑根节点
     * @param treeData
     */
    hdlEditRootNode({treeData}) {
        this.selectedNode = null;
        this.hdlAddNode(null, {treeData});
    }

    /**
     * 右侧列表 行 变化
     * @param dataList
     */
    hdlRowUpdate({dataList, type}) {

        if ('delete' === type) {
            // console.log('hdlRowUpdate')
            this.treeRef.addDeletedIds(this.tableRef.getDeleted());
        }
        // 如果左侧有选中的节点，则将右侧表格的数据，同步到左侧选中的节点
        let selNode = this.getSelNode();
        if (!isEmpty(selNode)) {
            const {key, children} = selNode;
            const tList = map(dataList, (item) => {
                const oldItem = find(children, (chItem) => (chItem && chItem.key === item.key));
                if (oldItem) {
                    return {...oldItem, ...item};
                } else {
                    return item;
                }
            });
            this.treeRef.updateDataByKey(key, tList, () => {
                // 展开当前选中的节点
                this.treeRef.expandNodeByKeys([key]);
            });
        } else {
            // 如果左侧没有选中节点，则将右侧表格的数据，同步到左侧的第一级节点
            const oldTreeData = this.treeRef.getAjaxData(false);
            const tList = map(dataList, (item) => {
                const oldItem = find(oldTreeData, (chItem) => (chItem && chItem.key === item.key));
                if (oldItem) {
                    return {...oldItem, ...item};
                } else {
                    return item;
                }
            });

            this.treeRef.setData(tList, null, false);
        }
    }

    /**
     * 右侧列表 单元格 变化
     * @param dataIndex
     * @param item
     * @param oldRecord
     * @param rowIndex
     * @param list
     */
    hdlCellUpdate(dataIndex, item, oldRecord, rowIndex, {list}) {
        // 根据改变的行数据，更新到左侧对应的节点中
        if (item && item.key) {
            this.treeRef.updateNodeByKey(item.key, omit(item, ['title']))
        }
    }

    mainRender(context) {
        const disabled = this.getPropsByName('disabled');
        const belong = {
            body: !isEmpty(this.props.fieldName),
            bodyFieldName: this.props.fieldName,
            ...(this.props.belong || {})
        };

        return (
            <Row style={{flexFlow: 'nowrap', ...(this.props.style || {})}}>
                <Col flex={'250px'} style={{paddingLeft: 8, paddingBottom: 8}}>
                    <EditableTree
                        belong={belong}
                        style={{height: '100%'}}
                        border={true}
                        disabled={disabled}
                        onRef={(ref) => {
                            this.treeRef = ref;
                        }}
                        afterSelect={this.hdlSelectNode.bind(this)}
                        onAddNode={this.hdlAddingNode.bind(this)}
                        afterAddNode={this.hdlAddNode.bind(this)}
                        afterDeleteNode={this.hdlDelNode.bind(this)}
                        afterSaveNode={this.hdlSaveNode.bind(this)}
                        onEditRootNode={this.hdlEditRootNode.bind(this)}
                    />
                </Col>
                <Col flex={'auto'} style={{overflow: 'auto', padding: '0 8px'}}>
                    <DetailTable
                        appins={this.props.appins}
                        belong={belong}
                        style={{height: '100%'}}
                        disabled={disabled}
                        onRef={(ref) => {
                            this.tableRef = ref;
                        }}
                        getDefaultRecord={(record) => {
                            const getDefaultRecord = this.getPropsByName('getDefaultRecord');
                            if (isFunction(getDefaultRecord)) {
                                return getDefaultRecord(record, {
                                    parentNode: this.getSelNode(),
                                    treeData: this.treeRef.getAjaxData()
                                }, this);
                            } else {
                                const obj = this.treeRef.getNewTreeNode();
                                const selNode = this.getSelNode();
                                if (selNode && selNode.isn) {
                                    obj.parentIsn = selNode.isn;
                                }
                                return obj;
                            }
                        }}
                        onCellUpdate={this.hdlCellUpdate.bind(this)}
                        onRowUpdate={this.hdlRowUpdate.bind(this)}
                        {...pick(this.props, [...keys(compConfig.basic), 'appins', 'customprops'])}
                        {...(this.props.tableProps || {})}
                    />
                </Col>
            </Row>
        )
    };

}